/*
 * CALLBACKS.C
 *
 * Colorful, sorted and optionally rich directory enumeration
 * for Windows.
 *
 * This module implements functions to collect, display, sort, and deserialize
 * individual data types associated with files that we can enumerate.
 *
 * Copyright (c) 2014-16 Malcolm J. Smith
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "sdir.h"

const SDIR_ATTRPAIR AttrPairs[] = {
    {FILE_ATTRIBUTE_ARCHIVE,           'A'},
    {FILE_ATTRIBUTE_READONLY,          'R'},
    {FILE_ATTRIBUTE_HIDDEN,            'H'},
    {FILE_ATTRIBUTE_SYSTEM,            'S'},
    {FILE_ATTRIBUTE_DIRECTORY,         'D'},
    {FILE_ATTRIBUTE_COMPRESSED,        'C'},
    {FILE_ATTRIBUTE_ENCRYPTED,         'E'},
    {FILE_ATTRIBUTE_OFFLINE,           'O'},
    {FILE_ATTRIBUTE_REPARSE_POINT,     'r'},
    {FILE_ATTRIBUTE_SPARSE_FILE,       's'},
    {FILE_ATTRIBUTE_INTEGRITY_STREAM,  'I'},
    };

DWORD
SdirGetNumAttrPairs()
{
    return sizeof(AttrPairs)/sizeof(AttrPairs[0]);
}

const SDIR_ATTRPAIR PermissionPairs[] = {
    {FILE_READ_DATA,                   'R'},
    {FILE_READ_ATTRIBUTES,             'r'},
    {FILE_WRITE_DATA,                  'W'},
    {FILE_WRITE_ATTRIBUTES,            'w'},
    {FILE_APPEND_DATA,                 'A'},
    {FILE_EXECUTE,                     'X'},
    {DELETE,                           'D'},
    };

DWORD
SdirGetNumPermissionPairs()
{
    return sizeof(PermissionPairs)/sizeof(PermissionPairs[0]);
}


//
//  Sorting support
//

DWORD
SdirCompareAccessDate (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareDate(&Left->AccessTime, &Right->AccessTime);
}

DWORD
SdirCompareAccessTime (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareTime(&Left->AccessTime, &Right->AccessTime);
}

DWORD
SdirCompareAllocatedRangeCount (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->AllocatedRangeCount, (PULARGE_INTEGER)&Right->AllocatedRangeCount);
}

DWORD
SdirCompareAllocationSize (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->AllocationSize, (PULARGE_INTEGER)&Right->AllocationSize);
}

DWORD
SdirCompareArch (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->Architecture < Right->Architecture) {
        return SDIR_LESS_THAN;
    } else if (Left->Architecture > Right->Architecture) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareCompressionAlgorithm (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->CompressionAlgorithm < Right->CompressionAlgorithm) {
        return SDIR_LESS_THAN;
    } else if (Left->CompressionAlgorithm > Right->CompressionAlgorithm) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareCompressedFileSize (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->CompressedFileSize, (PULARGE_INTEGER)&Right->CompressedFileSize);
}

DWORD
SdirCompareCreateDate (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareDate(&Left->CreateTime, &Right->CreateTime);
}

DWORD
SdirCompareCreateTime (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareTime(&Left->CreateTime, &Right->CreateTime);
}

DWORD
SdirCompareEffectivePermissions (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->EffectivePermissions < Right->EffectivePermissions) {
        return SDIR_LESS_THAN;
    } else if (Left->EffectivePermissions > Right->EffectivePermissions) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareFileAttributes (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->FileAttributes < Right->FileAttributes) {
        return SDIR_LESS_THAN;
    } else if (Left->FileAttributes > Right->FileAttributes) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareFileExtension (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareString(Left->Extension, Right->Extension);
}

DWORD
SdirCompareFileId (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->FileId, (PULARGE_INTEGER)&Right->FileId);
}

DWORD
SdirCompareFileName (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareString(Left->FileName, Right->FileName);
}

DWORD
SdirCompareFileSize (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->FileSize, (PULARGE_INTEGER)&Right->FileSize);
}

DWORD
SdirCompareFragmentCount (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->FragmentCount, (PULARGE_INTEGER)&Right->FragmentCount);
}

DWORD
SdirCompareLinkCount (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->LinkCount < Right->LinkCount) {
        return SDIR_LESS_THAN;
    } else if (Left->LinkCount > Right->LinkCount) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareObjectId (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    int result = memcmp(&Left->ObjectId, &Right->ObjectId, sizeof(Left->ObjectId));
    
    if (result < 0) {
        return SDIR_LESS_THAN;
    } else if (result > 0) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareOsVersion (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->OsVersionHigh < Right->OsVersionHigh) {
        return SDIR_LESS_THAN;
    } else if (Left->OsVersionHigh > Right->OsVersionHigh) {
        return SDIR_GREATER_THAN;
    }

    if (Left->OsVersionLow < Right->OsVersionLow) {
        return SDIR_LESS_THAN;
    } else if (Left->OsVersionLow > Right->OsVersionLow) {
        return SDIR_GREATER_THAN;
    }

    return SDIR_EQUAL;
}

DWORD
SdirCompareOwner (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareString(Left->Owner, Right->Owner);
}

DWORD
SdirCompareReparseTag (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->ReparseTag < Right->ReparseTag) {
        return SDIR_LESS_THAN;
    } else if (Left->ReparseTag > Right->ReparseTag) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareShortName (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareString(Left->ShortFileName, Right->ShortFileName);
}

DWORD
SdirCompareSubsystem (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->Subsystem < Right->Subsystem) {
        return SDIR_LESS_THAN;
    } else if (Left->Subsystem > Right->Subsystem) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareStreamCount (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if (Left->StreamCount < Right->StreamCount) {
        return SDIR_LESS_THAN;
    } else if (Left->StreamCount > Right->StreamCount) {
        return SDIR_GREATER_THAN;
    }
    return SDIR_EQUAL;
}

DWORD
SdirCompareUsn (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->Usn, (PULARGE_INTEGER)&Right->Usn);
}

DWORD
SdirCompareVersion (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareLargeInt((PULARGE_INTEGER)&Left->FileVersion, (PULARGE_INTEGER)&Right->FileVersion);
}

DWORD
SdirCompareWriteDate (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareDate(&Left->WriteTime, &Right->WriteTime);
}

DWORD
SdirCompareWriteTime (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    return SdirCompareTime(&Left->WriteTime, &Right->WriteTime);
}

DWORD
SdirBitwiseEffectivePermissions (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if ((Left->EffectivePermissions & Right->EffectivePermissions) == Right->EffectivePermissions) {
        return SDIR_EQUAL;
    }
    return SDIR_NOT_EQUAL;
}

DWORD
SdirBitwiseFileAttributes (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    if ((Left->FileAttributes & Right->FileAttributes) == Right->FileAttributes) {
        return SDIR_EQUAL;
    }
    return SDIR_NOT_EQUAL;
}

TCHAR
SdirGetUpcasedCharFromString (
    __in LPTSTR Str,
    __in DWORD Index
    )
{
    TCHAR Char = Str[Index];

    if (Char >= 'a' && Char <= 'z') {
        Char = Char - 'a' + 'A';
    }

    return Char;
}

DWORD
SdirBitwiseFileName (
    __in PSDIR_DIRENT Left,
    __in PSDIR_DIRENT Right
    )
{
    DWORD LeftIndex, RightIndex;

    TCHAR CompareLeft;
    TCHAR CompareRight;

    LeftIndex = 0;
    RightIndex = 0;

    while (Left->FileName[LeftIndex] != '\0' && Right->FileName[RightIndex] != '\0') {

        CompareLeft = SdirGetUpcasedCharFromString(Left->FileName, LeftIndex);
        CompareRight = SdirGetUpcasedCharFromString(Right->FileName, RightIndex);

        LeftIndex++;
        RightIndex++;

        if (CompareRight == '?') {

            //
            //  '?' matches with everything.  We've already advanced to the next
            //  char, so continue.
            //

        } else if (CompareRight == '*') {

            //
            //  Skip one char so Right is the one after *.  Left should compare
            //  the character it's currently on.  Keep going through Left until
            //  we find the char in Right
            //

            LeftIndex--;
            CompareRight = SdirGetUpcasedCharFromString(Right->FileName, RightIndex);
            CompareLeft = SdirGetUpcasedCharFromString(Left->FileName, LeftIndex);

            while (CompareLeft != CompareRight && CompareLeft != '\0') {
                LeftIndex++;
                CompareLeft = SdirGetUpcasedCharFromString(Left->FileName, LeftIndex);
            }

        } else {
            if (CompareLeft != CompareRight) {
                return SDIR_NOT_EQUAL;
            }
        }

    }

    if (Left->FileName[LeftIndex] == '\0' && Right->FileName[RightIndex] == '\0') {
        return SDIR_EQUAL;
    }

    return SDIR_NOT_EQUAL;
}


//
//  File enumeration support
//

BOOL
SdirCollectAccessTime (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    FILETIME tmp;

    FileTimeToLocalFileTime(&FindData->ftLastAccessTime, &tmp);
    FileTimeToSystemTime(&tmp, &Entry->AccessTime);
    return TRUE;
}

BOOL
SdirCollectAllocatedRangeCount (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;

    Entry->AllocatedRangeCount.HighPart = 0;
    Entry->AllocatedRangeCount.LowPart = 0;

    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES|FILE_READ_DATA,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {

        FILE_ALLOCATED_RANGE_BUFFER StartBuffer;
        union {
            FILE_ALLOCATED_RANGE_BUFFER Extents[1];
            UCHAR Buffer[2048];
        } u;
        DWORD BytesReturned;
        DWORD ElementCount;

        LARGE_INTEGER PriorRunLength = {0};
        LARGE_INTEGER PriorRunOffset = {0};

        StartBuffer.FileOffset.QuadPart = 0;
        StartBuffer.Length.LowPart = FindData->nFileSizeLow;
        StartBuffer.Length.HighPart = FindData->nFileSizeHigh;

        while ((DeviceIoControl(hFile, FSCTL_QUERY_ALLOCATED_RANGES, &StartBuffer, sizeof(StartBuffer), &u.Extents, sizeof(u), &BytesReturned, NULL) || GetLastError() == ERROR_MORE_DATA) &&
               BytesReturned > 0) {

            ElementCount = BytesReturned / sizeof(FILE_ALLOCATED_RANGE_BUFFER);

            // 
            //  Look through the extents.  If it's not a sparse hole, record it as a 
            //  fragment.  If it's also discontiguous with the previous run, count it as a
            //  fragment.
            //

            for (BytesReturned = 0; BytesReturned < ElementCount; BytesReturned++) {

                if (u.Extents[BytesReturned].FileOffset.QuadPart == 0 ||
                    PriorRunOffset.QuadPart + PriorRunLength.QuadPart != u.Extents[BytesReturned].FileOffset.QuadPart) {

                    if (Entry->AllocatedRangeCount.LowPart == (DWORD)-1) {
                        Entry->AllocatedRangeCount.HighPart++;
                    }
                    Entry->AllocatedRangeCount.LowPart++;
                }

                PriorRunLength.QuadPart = u.Extents[BytesReturned].Length.QuadPart;
                PriorRunOffset.QuadPart = u.Extents[BytesReturned].FileOffset.QuadPart;
            }

            StartBuffer.FileOffset.QuadPart = u.Extents[ElementCount - 1].FileOffset.QuadPart + u.Extents[ElementCount - 1].Length.QuadPart;

            if ((ULONG)StartBuffer.FileOffset.HighPart > FindData->nFileSizeHigh ||
                ((ULONG)StartBuffer.FileOffset.HighPart == FindData->nFileSizeHigh &&
                 StartBuffer.FileOffset.LowPart >= FindData->nFileSizeLow)) {

                break;
            }
        }

        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectAllocationSize (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    BOOL RealAllocSize = FALSE;

    if (Opts->GetFileInformationByHandleEx) {

        HANDLE hFile;

        hFile = CreateFile(FullPath,
                           FILE_READ_ATTRIBUTES,
                           FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                           NULL,
                           OPEN_EXISTING,
                           FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                           NULL);
    
        if (hFile != INVALID_HANDLE_VALUE) {
            FILE_STANDARD_INFO StandardInfo;
    
            if (Opts->GetFileInformationByHandleEx(hFile, FileStandardInfo, &StandardInfo, sizeof(StandardInfo))) {
                Entry->AllocationSize = StandardInfo.AllocationSize;
                RealAllocSize = TRUE;
            }

            CloseHandle(hFile);
        }
    }

    if (!RealAllocSize) {
        ULONG BytesPerSector;
        ULONG SectorsPerCluster;
        ULONG FreeClusters;
        ULONG TotalClusters;
        ULONG ClusterSize;

        GetDiskFreeSpace(Opts->ParentName, &SectorsPerCluster, &BytesPerSector, &FreeClusters, &TotalClusters);

        ClusterSize = SectorsPerCluster * BytesPerSector;

        Entry->AllocationSize.LowPart = FindData->nFileSizeLow;
        Entry->AllocationSize.HighPart = FindData->nFileSizeHigh;

        SdirFileSizeFromLargeInt(&Entry->AllocationSize) = (SdirFileSizeFromLargeInt(&Entry->AllocationSize) + ClusterSize - 1) & (~(ClusterSize - 1));
    }

    return TRUE;
}

typedef struct _SDIR_PE_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER ImageHeader;
    IMAGE_OPTIONAL_HEADER OptionalHeader;
} SDIR_PE_HEADERS, *PSDIR_PE_HEADERS;

BOOL
SdirCapturePeHeaders (
    __in LPCTSTR FullPath,
    __out PSDIR_PE_HEADERS PeHeaders
    )
{
    HANDLE hFileRead;
    IMAGE_DOS_HEADER DosHeader;
    DWORD BytesReturned;

    //
    //  We want the earlier handle to be attribute only so we can
    //  operate on directories, but we need data for this, so we
    //  end up with two handles.
    //

    hFileRead = CreateFile(FullPath,
                           FILE_READ_ATTRIBUTES|FILE_READ_DATA,
                           FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                           NULL,
                           OPEN_EXISTING,
                           FILE_FLAG_BACKUP_SEMANTICS,
                           NULL);

    if (hFileRead != INVALID_HANDLE_VALUE) {

        if (ReadFile(hFileRead, &DosHeader, sizeof(DosHeader), &BytesReturned, NULL) &&
            BytesReturned == sizeof(DosHeader) &&
            DosHeader.e_magic == IMAGE_DOS_SIGNATURE &&
            DosHeader.e_lfanew != 0) {

            SetFilePointer(hFileRead, DosHeader.e_lfanew, NULL, FILE_BEGIN);

            if (ReadFile(hFileRead, PeHeaders, sizeof(SDIR_PE_HEADERS), &BytesReturned, NULL) &&
                BytesReturned == sizeof(SDIR_PE_HEADERS) &&
                PeHeaders->Signature == IMAGE_NT_SIGNATURE &&
                PeHeaders->ImageHeader.SizeOfOptionalHeader >= FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, Subsystem)) {

                CloseHandle(hFileRead);
                return TRUE;

            }
        }
        CloseHandle(hFileRead);
    }
    return FALSE;
}

BOOL
SdirCollectArch (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    SDIR_PE_HEADERS PeHeaders;

    Entry->OsVersionHigh = 0;
    Entry->OsVersionLow = 0;

    if (SdirCapturePeHeaders(FullPath, &PeHeaders)) {

        Entry->Architecture = PeHeaders.ImageHeader.Machine;
    }

    return TRUE;
}

BOOL
SdirCollectCompressionAlgorithm (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;

    Entry->CompressionAlgorithm = SdirCompressionNone;

    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {

        USHORT NtfsCompressionAlgorithm;
        DWORD BytesReturned;
        struct {
            WOF_EXTERNAL_INFO WofHeader;
            union {
                WIM_PROVIDER_EXTERNAL_INFO WimInfo;
                FILE_PROVIDER_EXTERNAL_INFO FileInfo;
            } u;
        } WofInfo;

        if (DeviceIoControl(hFile, FSCTL_GET_COMPRESSION, NULL, 0, &NtfsCompressionAlgorithm, sizeof(NtfsCompressionAlgorithm), &BytesReturned, NULL)) {

            if (NtfsCompressionAlgorithm == COMPRESSION_FORMAT_LZNT1) {
                Entry->CompressionAlgorithm = SdirCompressionLznt;
            } else if (NtfsCompressionAlgorithm != COMPRESSION_FORMAT_NONE) {
                Entry->CompressionAlgorithm = SdirCompressionNtfsUnknown;
            }
        }

        if (Entry->CompressionAlgorithm == SdirCompressionNone) {
            if (DeviceIoControl(hFile, FSCTL_GET_EXTERNAL_BACKING, NULL, 0, &WofInfo, sizeof(WofInfo), &BytesReturned, NULL)) {
    
                if (WofInfo.WofHeader.Provider == WOF_PROVIDER_WIM) {
                    Entry->CompressionAlgorithm = SdirCompressionWim;
                } else if (WofInfo.WofHeader.Provider == WOF_PROVIDER_FILE) {
                    if (WofInfo.u.FileInfo.Algorithm == FILE_PROVIDER_COMPRESSION_XPRESS4K) {
                        Entry->CompressionAlgorithm = SdirCompressionXpress4k;
                    } else if (WofInfo.u.FileInfo.Algorithm == FILE_PROVIDER_COMPRESSION_XPRESS8K) {
                        Entry->CompressionAlgorithm = SdirCompressionXpress8k;
                    } else if (WofInfo.u.FileInfo.Algorithm == FILE_PROVIDER_COMPRESSION_XPRESS16K) {
                        Entry->CompressionAlgorithm = SdirCompressionXpress16k;
                    } else if (WofInfo.u.FileInfo.Algorithm == FILE_PROVIDER_COMPRESSION_LZX) {
                        Entry->CompressionAlgorithm = SdirCompressionLzx;
                    } else {
                        Entry->CompressionAlgorithm = SdirCompressionWofFileUnknown;
                    }
                } else {
                    Entry->CompressionAlgorithm = SdirCompressionWofUnknown;
                }
            }
        }

        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectCompressedFileSize (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    Entry->CompressedFileSize.LowPart = FindData->nFileSizeLow;
    Entry->CompressedFileSize.HighPart = FindData->nFileSizeHigh;

    if (Opts->GetCompressedFileSize) {
        Entry->CompressedFileSize.LowPart = Opts->GetCompressedFileSize(FullPath, (PDWORD)&Entry->CompressedFileSize.HighPart);

        if (Entry->CompressedFileSize.LowPart == INVALID_FILE_SIZE) {
            Entry->CompressedFileSize.LowPart = FindData->nFileSizeLow;
            Entry->CompressedFileSize.HighPart = FindData->nFileSizeHigh;
        }
    }

    return TRUE;
}

BOOL
SdirCollectCreateTime (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    FILETIME tmp;

    FileTimeToLocalFileTime(&FindData->ftCreationTime, &tmp);
    FileTimeToSystemTime(&tmp, &Entry->CreateTime);
    return TRUE;
}

BOOL
SdirCollectEffectivePermissions (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{

    //
    //  Allocate some buffers on the stack to hold the user SID,
    //  and one for the security descriptor which we can reallocate
    //  as needed.
    //

    UCHAR LocalSecurityDescriptor[512];
    PUCHAR SecurityDescriptor = NULL;
    DWORD dwSdRequired = 0;
    HANDLE TokenHandle = NULL;
    DWORD i;
    BOOL AccessGranted;
    ACCESS_MASK UnderstoodPermissions = 0;
    GENERIC_MAPPING Mapping = {0};
    PRIVILEGE_SET Privilege;
    DWORD PrivilegeLength = sizeof(Privilege);

    Entry->EffectivePermissions = 0;

    SecurityDescriptor = LocalSecurityDescriptor;

    if (!GetFileSecurity(FullPath, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, SecurityDescriptor, sizeof(LocalSecurityDescriptor), &dwSdRequired)) {
        if (dwSdRequired != 0) {
            SecurityDescriptor = HeapAlloc(GetProcessHeap(), 0, dwSdRequired);
    
            if (!GetFileSecurity(FullPath, OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, SecurityDescriptor, dwSdRequired, &dwSdRequired)) {
                goto Exit;
            }
        } else {
            goto Exit;
        }
    }

    if (!ImpersonateSelf(SecurityIdentification)) {
        goto Exit;
    }
    if (!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &TokenHandle)) {
        RevertToSelf();
        goto Exit;
    }

    AccessCheck(SecurityDescriptor, TokenHandle, MAXIMUM_ALLOWED, &Mapping, &Privilege, &PrivilegeLength, &Entry->EffectivePermissions, &AccessGranted);

    //
    //  Strip off any permissions we don't understand so that tests for
    //  equality are meaningful
    //

    for (i = 0; i < SdirGetNumPermissionPairs(); i++) {
        UnderstoodPermissions |= PermissionPairs[i].FileAttribute;
    }

    Entry->EffectivePermissions &= UnderstoodPermissions;

Exit:
    if (TokenHandle != NULL) {
        CloseHandle(TokenHandle);
        RevertToSelf();
    }
    if (SecurityDescriptor != NULL && SecurityDescriptor != LocalSecurityDescriptor) {
        HeapFree(GetProcessHeap(), 0, SecurityDescriptor);
    }

    return TRUE;
}

BOOL
SdirCollectFileAttributes (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    DWORD i;

    //
    //  We do this bit by bit to ensure that we don't have file attributes
    //  recorded that we don't understand.  This allows us to perform
    //  equality comparisons where the result is understandable to the user
    //  in that it can be specified and displayed.
    //

    Entry->FileAttributes = 0;
    for (i = 0; i < SdirGetNumAttrPairs(); i++) {
        if (FindData->dwFileAttributes & AttrPairs[i].FileAttribute) {
            Entry->FileAttributes |= AttrPairs[i].FileAttribute;
        }
    }
    return TRUE;
}

BOOL
SdirCollectFileExtension (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    return TRUE;
}

BOOL
SdirCollectFileId (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;

    Entry->FileId.QuadPart = 0;

    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {
        BY_HANDLE_FILE_INFORMATION FileInfo;

        if (GetFileInformationByHandle(hFile, &FileInfo)) {
            Entry->FileId.LowPart = FileInfo.nFileIndexLow;
            Entry->FileId.HighPart = FileInfo.nFileIndexHigh;
        }

        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectFileName (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    Entry->FileNameLengthInChars = (DWORD)_tcslen(FindData->cFileName);
    if (Entry->FileNameLengthInChars >= SDIR_MAX_FILE_NAME - 1) {
        Entry->FileNameLengthInChars = SDIR_MAX_FILE_NAME - 2;
    }
    memcpy(Entry->FileName, FindData->cFileName, Entry->FileNameLengthInChars * sizeof(TCHAR));
    Entry->FileName[Entry->FileNameLengthInChars] = '\0';

    Entry->Extension = _tcsrchr(Entry->FileName, '.');

    //
    //  For simplicity's sake, if we have no extension set the field
    //  to the end of string, so we'll see a valid pointer of nothing.
    //

    if (Entry->Extension == NULL) {
        Entry->Extension = Entry->FileName + Entry->FileNameLengthInChars;
    } else {
        Entry->Extension++;
    }

    return TRUE;
}

BOOL
SdirCollectFileSize (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    Entry->FileSize.LowPart = FindData->nFileSizeLow;
    Entry->FileSize.HighPart = FindData->nFileSizeHigh;
    return TRUE;
}

BOOL
SdirCollectFragmentCount (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;

    Entry->FragmentCount.HighPart = 0;
    Entry->FragmentCount.LowPart = 0;

    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {

        STARTING_VCN_INPUT_BUFFER StartBuffer;
        union {
            RETRIEVAL_POINTERS_BUFFER Extents;
            UCHAR Buffer[2048];
        } u;
        DWORD BytesReturned;

        LARGE_INTEGER PriorRunLength = {0};
        LARGE_INTEGER PriorNextVcn = {0};
        LARGE_INTEGER PriorLcn = {0};

        StartBuffer.StartingVcn.QuadPart = 0;

        while ((DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &StartBuffer, sizeof(StartBuffer), &u.Extents, sizeof(u), &BytesReturned, NULL) || GetLastError() == ERROR_MORE_DATA) &&
               u.Extents.ExtentCount > 0) {

            // 
            //  Look through the extents.  If it's not a sparse hole, record it as a 
            //  fragment.  If it's also discontiguous with the previous run, count it as a
            //  fragment.
            //

            for (BytesReturned = 0; BytesReturned < u.Extents.ExtentCount; BytesReturned++) {
                if (u.Extents.Extents[BytesReturned].Lcn.HighPart != (DWORD)-1 &&
                    u.Extents.Extents[BytesReturned].Lcn.LowPart != (DWORD)-1) {

                    if (PriorLcn.QuadPart + PriorRunLength.QuadPart != u.Extents.Extents[BytesReturned].Lcn.QuadPart) {
                        if (Entry->FragmentCount.LowPart == (DWORD)-1) {
                            Entry->FragmentCount.HighPart++;
                        }
                        Entry->FragmentCount.LowPart++;
                    }
                }

                PriorRunLength.QuadPart = u.Extents.Extents[BytesReturned].NextVcn.QuadPart - PriorNextVcn.QuadPart;
                PriorNextVcn = u.Extents.Extents[BytesReturned].NextVcn;
                PriorLcn = u.Extents.Extents[BytesReturned].Lcn;
            }

            StartBuffer.StartingVcn.QuadPart = u.Extents.Extents[u.Extents.ExtentCount - 1].NextVcn.QuadPart;
        }

        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectLinkCount (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;

    Entry->LinkCount = 0;

    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {
        BY_HANDLE_FILE_INFORMATION FileInfo;

        if (GetFileInformationByHandle(hFile, &FileInfo)) {
            Entry->LinkCount = FileInfo.nNumberOfLinks;
        }

        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectObjectId (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;
    FILE_OBJECTID_BUFFER Buffer;
    DWORD BytesReturned;

    ZeroMemory(&Entry->ObjectId, sizeof(Entry->ObjectId));

    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {
        if (DeviceIoControl(hFile, FSCTL_GET_OBJECT_ID, NULL, 0, &Buffer, sizeof(Buffer), &BytesReturned, NULL)) {
            CopyMemory(&Entry->ObjectId, &Buffer.ObjectId, sizeof(Buffer.ObjectId));
        }
        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectOsVersion (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    SDIR_PE_HEADERS PeHeaders;

    Entry->OsVersionHigh = 0;
    Entry->OsVersionLow = 0;

    if (SdirCapturePeHeaders(FullPath, &PeHeaders)) {

        Entry->OsVersionHigh = PeHeaders.OptionalHeader.MajorSubsystemVersion;
        Entry->OsVersionLow = PeHeaders.OptionalHeader.MinorSubsystemVersion;
    }

    return TRUE;
}

BOOL
SdirCollectOwner (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{

    //
    //  Allocate some buffers on the stack to hold the user name, domain name
    //  and owner portion of the security descriptor.  In the first case, this
    //  is to help ensure we have space to store the whole thing; in the
    //  second case, this function crashes without a buffer even if we discard
    //  the result; and the descriptor here doesn't contain the ACL and only
    //  needs to be big enough to hold one variable sized owner SID.
    //

    TCHAR UserName[128];
    DWORD NameLength = sizeof(UserName)/sizeof(UserName[0]);
    TCHAR DomainName[128];
    DWORD DomainLength = sizeof(DomainName)/sizeof(DomainName[0]);
    UCHAR SecurityDescriptor[256];
    DWORD dwSdRequired = 0;
    BOOL OwnerDefaulted;
    PSID pOwnerSid;
    SID_NAME_USE eUse;

    UserName[0] = '\0';
    Entry->Owner[0] = '\0';

    if (GetFileSecurity(FullPath, OWNER_SECURITY_INFORMATION, SecurityDescriptor, sizeof(SecurityDescriptor), &dwSdRequired)) {
        if (GetSecurityDescriptorOwner(SecurityDescriptor, &pOwnerSid, &OwnerDefaulted)) {
            if (LookupAccountSid(NULL, pOwnerSid, UserName, &NameLength, DomainName, &DomainLength, &eUse)) {
                UserName[sizeof(Entry->Owner) - 1] = '\0';
                memcpy(Entry->Owner, UserName, sizeof(Entry->Owner));
            }
        }
    }

    return TRUE;
}

BOOL
SdirCollectReparseTag (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    if (Entry->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
        Entry->ReparseTag = FindData->dwReserved0;
    } else {
        Entry->ReparseTag = 0;
    }
    return TRUE;
}

BOOL
SdirCollectShortName (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    if (FindData->cAlternateFileName[0] == '\0' && _tcslen(FindData->cFileName) <= 12) {
        CopyMemory(Entry->ShortFileName, FindData->cFileName, sizeof(FindData->cAlternateFileName));
    } else {
        CopyMemory(Entry->ShortFileName, FindData->cAlternateFileName, sizeof(FindData->cAlternateFileName));
    }
    return TRUE;
}

BOOL
SdirCollectSubsystem (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    SDIR_PE_HEADERS PeHeaders;

    Entry->Subsystem = 0;

    if (SdirCapturePeHeaders(FullPath, &PeHeaders)) {

        Entry->Subsystem = PeHeaders.OptionalHeader.Subsystem;
    }

    return TRUE;
}

#ifdef UNICODE
BOOL
SdirCollectStreamCount (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFind;
    SDIR_WIN32_FIND_STREAM_DATA FindStreamData;

    Entry->StreamCount = 0;

    //
    //  These APIs are Unicode only.  We could do an ANSI to Unicode thunk here, but
    //  since Unicode is the default build and ANSI is only useful for older systems
    //  (where this API won't exist) there doesn't seem much point.
    //

    if (Opts->FindFirstStreamW && Opts->FindNextStreamW) {
        hFind = Opts->FindFirstStreamW(FullPath, 0, &FindStreamData, 0);
        if (hFind != INVALID_HANDLE_VALUE) {

            do {
                Entry->StreamCount++;
            } while (Opts->FindNextStreamW(hFind, &FindStreamData));
        }

        FindClose(hFind);
    }

    return TRUE;
}
#endif

BOOL
SdirCollectUsn (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    HANDLE hFile;

    Entry->Usn.QuadPart = 0;
    hFile = CreateFile(FullPath,
                       FILE_READ_ATTRIBUTES,
                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_OPEN_NO_RECALL,
                       NULL);

    if (hFile != INVALID_HANDLE_VALUE) {

        struct {
            SDIR_USN_RECORD UsnRecord;
            WCHAR FileName[SDIR_MAX_FILE_NAME];
        } s1;
        DWORD BytesReturned;

        if (DeviceIoControl(hFile, FSCTL_READ_FILE_USN_DATA, NULL, 0, &s1, sizeof(s1), &BytesReturned, NULL)) {
            Entry->Usn.QuadPart = s1.UsnRecord.Usn.QuadPart;
        }

        CloseHandle(hFile);
    }
    return TRUE;
}

BOOL
SdirCollectVersion (
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    DWORD Junk;
    PVOID Buffer;
    DWORD VerSize = GetFileVersionInfoSize((LPTSTR)FullPath, &Junk);
    VS_FIXEDFILEINFO * RootBlock;

    Entry->FileVersion.QuadPart = 0;
    Entry->FileVersionFlags = 0;

    Buffer = HeapAlloc(GetProcessHeap(), 0, VerSize);
    if (Buffer != NULL) {
        if (GetFileVersionInfo((LPTSTR)FullPath, 0, VerSize, Buffer)) {
            if (VerQueryValue(Buffer, _T("\\"), (PVOID*)&RootBlock, (PUINT)&Junk)) {
                Entry->FileVersion.HighPart = RootBlock->dwFileVersionMS;
                Entry->FileVersion.LowPart = RootBlock->dwFileVersionLS;
                Entry->FileVersionFlags = RootBlock->dwFileFlags & RootBlock->dwFileFlagsMask;
            }
        }
        HeapFree(GetProcessHeap(), 0, Buffer);
    }
    return TRUE;
}

BOOL
SdirCollectWriteTime(
    __inout PSDIR_DIRENT Entry,
    __in PWIN32_FIND_DATA FindData,
    __in LPCTSTR FullPath
    )
{
    FILETIME tmp;

    FileTimeToLocalFileTime(&FindData->ftLastWriteTime, &tmp);
    FileTimeToSystemTime(&tmp, &Entry->WriteTime);
    return TRUE;
}

BOOL
SdirCollectSummary(
    __in PSDIR_DIRENT Entry
    )
{

    //
    //  Don't count . and .. at all
    //

    if ((Entry->FileNameLengthInChars == 1 && Entry->FileName[0] == '.') ||
        (Entry->FileNameLengthInChars == 2 && Entry->FileName[0] == '.' && Entry->FileName[1] == '.')) {

        return TRUE;
    }

    //
    //  Otherwise, count either as a file or a dir
    //

    if (Entry->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        Summary->NumDirs++;
    } else {
        Summary->NumFiles++;
    }

    Summary->TotalSize += SdirFileSizeFromLargeInt(&Entry->FileSize);

    if (Opts->FtCompressedFileSize.Flags & SDIR_FEATURE_COLLECT) {
        Summary->CompressedSize += SdirFileSizeFromLargeInt(&Entry->CompressedFileSize);
    }

    return TRUE;
}

//
//  When criteria are specified to apply attributes, we need to load the specification
//  into a dummy dirent to perform comparisons against.  The below functions implement
//  these.
//

BOOL
SdirGenerateAccessDate(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    return SdirStringToDate(String, &Entry->AccessTime);
}

BOOL
SdirGenerateAccessTime(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    return SdirStringToTime(String, &Entry->AccessTime);
}

BOOL
SdirGenerateAllocatedRangeCount(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->AllocatedRangeCount = SdirStringToNum64(String, NULL);
    return TRUE;
}

BOOL
SdirGenerateAllocationSize(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->AllocationSize = SdirStringToFileSize(String);
    return TRUE;
}

BOOL
SdirGenerateArch(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    if (_tcsicmp(String, _T("None")) == 0) {
        Entry->Architecture = 0;
    } else if (_tcsicmp(String, _T("i386")) == 0) {
        Entry->Architecture = IMAGE_FILE_MACHINE_I386;
    } else if (_tcsicmp(String, _T("amd64")) == 0) {
        Entry->Architecture = IMAGE_FILE_MACHINE_AMD64;
    } else if (_tcsicmp(String, _T("arm")) == 0) {
        Entry->Architecture = IMAGE_FILE_MACHINE_ARMNT;
    } else if (_tcsicmp(String, _T("arm64")) == 0) {
        Entry->Architecture = IMAGE_FILE_MACHINE_ARM64;
    } else {
        return FALSE;
    }
    return TRUE;
}

BOOL
SdirGenerateCompressionAlgorithm(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{

    if (_tcsicmp(String, _T("None")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionNone;
    } else if (_tcsicmp(String, _T("LZNT")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionLznt;
    } else if (_tcsicmp(String, _T("NTFS")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionNtfsUnknown;
    } else if (_tcsicmp(String, _T("WIM")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionWim;
    } else if (_tcsicmp(String, _T("LZX")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionLzx;
    } else if (_tcsicmp(String, _T("Xp4")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionXpress4k;
    } else if (_tcsicmp(String, _T("Xp8")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionXpress8k;
    } else if (_tcsicmp(String, _T("Xp16")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionXpress16k;
    } else if (_tcsicmp(String, _T("File")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionWofFileUnknown;
    } else if (_tcsicmp(String, _T("Wof")) == 0) {
        Entry->CompressionAlgorithm = SdirCompressionWofUnknown;
    } else {
        return FALSE;
    }
    return TRUE;
}

BOOL
SdirGenerateCompressedFileSize(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->CompressedFileSize = SdirStringToFileSize(String);
    return TRUE;
}

BOOL
SdirGenerateCreateDate(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    return SdirStringToDate(String, &Entry->CreateTime);
}

BOOL
SdirGenerateCreateTime(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    return SdirStringToTime(String, &Entry->CreateTime);
}

BOOL
SdirGenerateEffectivePermissions(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    DWORD i;

    Entry->FileAttributes = 0;

    while (*String) {

        for (i = 0; i < SdirGetNumPermissionPairs(); i++) {
            if (*String == PermissionPairs[i].DisplayLetter) {
                Entry->EffectivePermissions |= PermissionPairs[i].FileAttribute;
            }
        }

        String++;
    }
    return TRUE;
}

BOOL
SdirGenerateFileAttributes(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    DWORD i;

    Entry->FileAttributes = 0;

    while (*String) {

        for (i = 0; i < SdirGetNumAttrPairs(); i++) {
            if (*String == AttrPairs[i].DisplayLetter) {
                Entry->FileAttributes |= AttrPairs[i].FileAttribute;
            }
        }

        String++;
    }
    return TRUE;
}

BOOL
SdirGenerateFileExtension(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    //
    //  Since we have one dirent per comparison, just shove the extension in
    //  the file name buffer and point the extension to it.  This buffer can't
    //  be used for anything else anyway.
    //

    _stprintf_s(Entry->FileName, sizeof(Entry->FileName)/sizeof(Entry->FileName[0]), _T("%s"), String);
    Entry->FileName[sizeof(Entry->FileName)/sizeof(Entry->FileName[0]) - 1] = '\0';
    Entry->Extension = Entry->FileName;

    return TRUE;
}

BOOL
SdirGenerateFileName(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->FileNameLengthInChars = _stprintf_s(Entry->FileName, sizeof(Entry->FileName)/sizeof(Entry->FileName[0]), _T("%s"), String);
    Entry->FileName[sizeof(Entry->FileName)/sizeof(Entry->FileName[0]) - 1] = '\0';

    return TRUE;
}

BOOL
SdirGenerateFileSize(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->FileSize = SdirStringToFileSize(String);
    return TRUE;
}

BOOL
SdirGenerateFragmentCount(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->FragmentCount = SdirStringToNum64(String, NULL);
    return TRUE;
}

BOOL
SdirGenerateLinkCount(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->LinkCount = SdirStringToNum32(String, NULL);
    return TRUE;
}

BOOL
SdirGenerateObjectId(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    UCHAR Buffer[16];
    if (SdirStringToHexBuffer(String, (PUCHAR)&Buffer, sizeof(Buffer), NULL)) {
        CopyMemory(Entry->ObjectId, Buffer, sizeof(Buffer));
    }
    return TRUE;
}

BOOL
SdirGenerateOsVersion(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    TCHAR * endptr = NULL;

    Entry->OsVersionHigh = (WORD)SdirStringToNum32(String, &endptr);

    if (*endptr == '.') {
        Entry->OsVersionLow = (WORD)SdirStringToNum32(endptr+1, NULL);
    }

    return TRUE;
}

BOOL
SdirGenerateOwner(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    _stprintf_s(Entry->Owner, sizeof(Entry->Owner)/sizeof(Entry->Owner[0]), _T("%s"), String);
    Entry->Owner[sizeof(Entry->Owner)/sizeof(Entry->Owner[0]) - 1] = '\0';

    return TRUE;
}

BOOL
SdirGenerateReparseTag(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->ReparseTag = SdirStringToNum32(String, NULL);
    return TRUE;
}

BOOL
SdirGenerateShortName(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    _stprintf_s(Entry->ShortFileName, sizeof(Entry->ShortFileName)/sizeof(Entry->ShortFileName[0]), _T("%s"), String);
    Entry->ShortFileName[sizeof(Entry->ShortFileName)/sizeof(Entry->ShortFileName[0]) - 1] = '\0';

    return TRUE;
}

ULONG
SdirGenerateSubsystem (
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    if (_tcsicmp(String, _T("None")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_UNKNOWN;
    } else if (_tcsicmp(String, _T("NT")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_NATIVE;
    } else if (_tcsicmp(String, _T("GUI")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
    } else if (_tcsicmp(String, _T("Cons")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
    } else if (_tcsicmp(String, _T("OS/2")) == 0 || _tcsicmp(String, _T("OS2")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_OS2_CUI;
    } else if (_tcsicmp(String, _T("Posx")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_POSIX_CUI;
    } else if (_tcsicmp(String, _T("w9x")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_NATIVE_WINDOWS;
    } else if (_tcsicmp(String, _T("CE")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CE_GUI;
    } else if (_tcsicmp(String, _T("EFIa")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION;
    } else if (_tcsicmp(String, _T("EFIb")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER;
    } else if (_tcsicmp(String, _T("EFId")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;
    } else if (_tcsicmp(String, _T("EFIr")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_ROM;
    } else if (_tcsicmp(String, _T("Xbox")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_XBOX;
    } else if (_tcsicmp(String, _T("Xbcc")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG;
    } else if (_tcsicmp(String, _T("Boot")) == 0) {
        Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION;
    } else {
        return FALSE;
    }

    return TRUE;
}

BOOL
SdirGenerateStreamCount(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->StreamCount = SdirStringToNum32(String, NULL);
    return TRUE;
}

BOOL
SdirGenerateUsn(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    Entry->Usn = SdirStringToNum64(String, NULL);
    return TRUE;
}

BOOL
SdirGenerateVersion(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    LARGE_INTEGER Version = {0};
    TCHAR * endptr = NULL;

    Version.HighPart = SdirStringToNum32(String, &endptr)<<16;

    if (*endptr == '.') {
        Version.HighPart = Version.HighPart + (WORD)SdirStringToNum32(endptr+1, &endptr);

        if (*endptr == '.') {
            Version.LowPart = SdirStringToNum32(endptr+1, &endptr)<<16;
            if (*endptr == '.') {
                Version.LowPart = Version.LowPart + (WORD)SdirStringToNum32(endptr+1, NULL);
            }
        }
    }

    Entry->FileVersion = Version;

    return TRUE;
}

BOOL
SdirGenerateWriteDate(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    return SdirStringToDate(String, &Entry->WriteTime);
}

BOOL
SdirGenerateWriteTime(
    __inout PSDIR_DIRENT Entry,
    __in LPCTSTR String
    )
{
    return SdirStringToTime(String, &Entry->WriteTime);
}


//
//  Specific formatting and sizing callbacks for each supported piece of file metadata.
//

ULONG
SdirDisplayFileAccessDate (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayFileDate(Buffer, Attributes, &Entry->AccessTime);
}

ULONG
SdirDisplayFileAccessTime (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayFileTime(Buffer, Attributes, &Entry->AccessTime);
}

ULONG
SdirDisplayAllocatedRangeCount (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR Str[8];

    if (Buffer) {

        //
        //  As a special hack to suppress the 'b' suffix, just display as
        //  a number unless it's six digits.  At that point we know we'll
        //  have a 'k' suffix or above.
        //

        if (SdirFileSizeFromLargeInt(&Entry->AllocatedRangeCount) <= 99999) { 
            _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %05i"), Entry->AllocatedRangeCount.LowPart);
            SdirPasteStr(Buffer, Str, Attributes, 6);
        } else {
            SdirDisplayGenericSize(Buffer, Attributes, &Entry->AllocatedRangeCount);
        }
    }
    return 6;
}

ULONG
SdirDisplayAllocationSize (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayGenericSize(Buffer, Attributes, &Entry->AllocationSize);
}

ULONG
SdirDisplayArch (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR StrBuf[8];
    LPTSTR StrFixed;

    if (Buffer) {
        switch(Entry->Architecture) {
            case IMAGE_FILE_MACHINE_UNKNOWN:
                StrFixed = _T("None ");
                break;
            case IMAGE_FILE_MACHINE_I386:
                StrFixed = _T("i386 ");
                break;
            case IMAGE_FILE_MACHINE_AMD64:
                StrFixed = _T("amd64");
                break;
            case IMAGE_FILE_MACHINE_ARMNT:
                StrFixed = _T("arm  ");
                break;
            case IMAGE_FILE_MACHINE_ARM64:
                StrFixed = _T("arm64");
                break;
            default:
                StrFixed = _T("New? ");
                break;
        }
        _stprintf_s(StrBuf, sizeof(StrBuf)/sizeof(StrBuf[0]), _T(" %5s"), StrFixed);
        SdirPasteStr(Buffer, StrBuf, Attributes, 6);
    }
    return 6;
}

ULONG
SdirDisplayCompressionAlgorithm (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR StrBuf[8];
    LPTSTR StrFixed;

    if (Buffer) {
        switch(Entry->CompressionAlgorithm) {
            case SdirCompressionNone:
                StrFixed = _T("None");
                break;
            case SdirCompressionLznt:
                StrFixed = _T("LZNT");
                break;
            case SdirCompressionNtfsUnknown:
                StrFixed = _T("NTFS");
                break;
            case SdirCompressionWim:
                StrFixed = _T("WIM ");
                break;
            case SdirCompressionLzx:
                StrFixed = _T("LZX ");
                break;
            case SdirCompressionXpress4k:
                StrFixed = _T("Xp4 ");
                break;
            case SdirCompressionXpress8k:
                StrFixed = _T("Xp8 ");
                break;
            case SdirCompressionXpress16k:
                StrFixed = _T("Xp16");
                break;
            case SdirCompressionWofFileUnknown:
                StrFixed = _T("File");
                break;
            case SdirCompressionWofUnknown:
                StrFixed = _T("Wof ");
                break;
            default:
                StrFixed = _T("BUG ");
                break;
        }
        _stprintf_s(StrBuf, sizeof(StrBuf)/sizeof(StrBuf[0]), _T(" %4s"), StrFixed);
        SdirPasteStr(Buffer, StrBuf, Attributes, 5);
    }
    return 5;
}

ULONG
SdirDisplayCompressedFileSize (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayGenericSize(Buffer, Attributes, &Entry->CompressedFileSize);
}

ULONG
SdirDisplayEffectivePermissions (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR StrAtts[32];
    DWORD i;

    if (Buffer) {
        StrAtts[0] = ' ';
        for (i = 0; i < SdirGetNumPermissionPairs(); i++) {
            if (Entry->EffectivePermissions & PermissionPairs[i].FileAttribute) {
                StrAtts[i + 1] = PermissionPairs[i].DisplayLetter;
            } else {
                StrAtts[i + 1] = '-';
            }
        }

        StrAtts[i + 1] = '\0';
    
        SdirPasteStr(Buffer, StrAtts, Attributes, SdirGetNumPermissionPairs() + 1);
    }
    return SdirGetNumPermissionPairs() + 1;
}

ULONG
SdirDisplayFileCreateDate (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayFileDate(Buffer, Attributes, &Entry->CreateTime);
}

ULONG
SdirDisplayFileCreateTime (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayFileTime(Buffer, Attributes, &Entry->CreateTime);
}

ULONG
SdirDisplayFileAttributes (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR StrAtts[32];
    DWORD i;

    if (Buffer) {
        StrAtts[0] = ' ';
        for (i = 0; i < SdirGetNumAttrPairs(); i++) {
            if (Entry->FileAttributes & AttrPairs[i].FileAttribute) {
                StrAtts[i + 1] = AttrPairs[i].DisplayLetter;
            } else {
                StrAtts[i + 1] = '-';
            }
        }

        StrAtts[i + 1] = '\0';
    
        SdirPasteStr(Buffer, StrAtts, Attributes, SdirGetNumAttrPairs() + 1);
    }
    return SdirGetNumAttrPairs() + 1;
}

ULONG
SdirDisplayFileId (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayHex64(Buffer, Attributes, &Entry->FileId);
}

ULONG
SdirDisplayFileSize (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    if (Buffer) {
        if (Entry->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
            if (Entry->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
                SdirPasteStr(Buffer, _T(" <LNK>"), Entry->RenderAttributes, 6);
            } else if (Entry->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
                SdirPasteStr(Buffer, _T(" <MNT>"), Entry->RenderAttributes, 6);
            } else {
                return SdirDisplayGenericSize(Buffer, Attributes, &Entry->FileSize);
            }
            return 6;
        } else if (Entry->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            SdirPasteStr(Buffer, _T(" <DIR>"), Entry->RenderAttributes, 6);
            return 6;
        } else {
            return SdirDisplayGenericSize(Buffer, Attributes, &Entry->FileSize);
        }
    }
    return 6;
}

ULONG
SdirDisplayFragmentCount (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR Str[8];

    if (Buffer) {

        //
        //  As a special hack to suppress the 'b' suffix, just display as
        //  a number unless it's six digits.  At that point we know we'll
        //  have a 'k' suffix or above.
        //

        if (SdirFileSizeFromLargeInt(&Entry->FragmentCount) <= 99999) { 
            _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %05i"), Entry->FragmentCount.LowPart);
            SdirPasteStr(Buffer, Str, Attributes, 6);
        } else {
            SdirDisplayGenericSize(Buffer, Attributes, &Entry->FragmentCount);
        }
    }
    return 6;
}

ULONG
SdirDisplayLinkCount (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR Str[5];

    if (Buffer) {
        if (Entry->LinkCount >= 1000) {
            _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" >1k"));
        } else {
            _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %03i"), Entry->LinkCount);
        }
        SdirPasteStr(Buffer, Str, Attributes, 4);
    }
    return 4;
}

ULONG
SdirDisplayObjectId (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    if (Buffer) {
        SdirDisplayGenericHexBuffer(Buffer, Attributes, (PUCHAR)&Entry->ObjectId, sizeof(Entry->ObjectId));
    }

    return 2 * sizeof(Entry->ObjectId) + 1;
}

ULONG
SdirDisplayOsVersion (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR Str[30];
    BYTE  ThisOsMajor = LOBYTE(LOWORD(Opts->OsVersion));
    BYTE  ThisOsMinor = HIBYTE(LOWORD(Opts->OsVersion));

    if (Buffer) {
        _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %02i.%02i"), Entry->OsVersionHigh, Entry->OsVersionLow);
    
        if (Entry->OsVersionHigh > ThisOsMajor ||
            (Entry->OsVersionHigh == ThisOsMajor && Entry->OsVersionLow > ThisOsMinor)) {
    
            Attributes = SDIR_FUTURE_VERSION_COLOR;
        }
    
        SdirPasteStr(Buffer, Str, Attributes, 6);
    }
    return 6;
}

ULONG
SdirDisplayOwner (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    ULONG CurrentChar = 0;
    DWORD ShortLength;

    if (Buffer) {
        SdirPasteStrAndPad(&Buffer[CurrentChar], NULL, 0, 0, 1);
    }
    CurrentChar++;

    if (Buffer) {
        ShortLength = (DWORD)_tcslen(Entry->Owner);

        SdirPasteStrAndPad(&Buffer[CurrentChar], Entry->Owner, Attributes, ShortLength, sizeof(Entry->Owner)/sizeof(Entry->Owner[0]) - 1);
    }
    CurrentChar += sizeof(Entry->Owner)/sizeof(Entry->Owner[0]) - 1;
    return CurrentChar;
}

ULONG
SdirDisplayReparseTag (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    ULONG Tag = 0;
    if (Entry) {
        Tag = Entry->ReparseTag;
    }
    return SdirDisplayHex32(Buffer, Attributes, Tag);
}

ULONG
SdirDisplayShortName (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    ULONG CurrentChar = 0;
    DWORD ShortLength;

    //
    //  Just because we like special cases, add a space only if both
    //  names are being displayed.
    //

    if (Opts->FtFileName.Flags & SDIR_FEATURE_DISPLAY) {
        if (Buffer) {
            SdirPasteStrAndPad(&Buffer[CurrentChar], NULL, 0, 0, 1);
        }
        CurrentChar++;
    }

    if (Buffer) {
        ShortLength = (DWORD)_tcslen(Entry->ShortFileName);

        SdirPasteStrAndPad(&Buffer[CurrentChar], Entry->ShortFileName, Attributes, ShortLength, 12);
    }
    CurrentChar += 12;
    return CurrentChar;
}

ULONG
SdirDisplaySubsystem (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR StrBuf[8];
    LPTSTR StrFixed;

    if (Buffer) {
        switch(Entry->Subsystem) {
            case IMAGE_SUBSYSTEM_UNKNOWN:
                StrFixed = _T("None");
                break;
            case IMAGE_SUBSYSTEM_NATIVE:
                StrFixed = _T("NT  ");
                break;
            case IMAGE_SUBSYSTEM_WINDOWS_GUI:
                StrFixed = _T("GUI ");
                break;
            case IMAGE_SUBSYSTEM_WINDOWS_CUI:
                StrFixed = _T("Cons");
                break;
            case IMAGE_SUBSYSTEM_OS2_CUI:
                StrFixed = _T("OS/2");
                break;
            case IMAGE_SUBSYSTEM_POSIX_CUI:
                StrFixed = _T("Posx");
                break;
            case IMAGE_SUBSYSTEM_NATIVE_WINDOWS:
                StrFixed = _T("w9x ");
                break;
            case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
                StrFixed = _T("CE  ");
                break;
            case IMAGE_SUBSYSTEM_EFI_APPLICATION:
                StrFixed = _T("EFIa");
                break;
            case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
                StrFixed = _T("EFIb");
                break;
            case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
                StrFixed = _T("EFId");
                break;
            case IMAGE_SUBSYSTEM_EFI_ROM:
                StrFixed = _T("EFIr");
                break;
            case IMAGE_SUBSYSTEM_XBOX:
                StrFixed = _T("Xbox");
                break;
            case IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG:
                StrFixed = _T("Xbcc");
                break;
            case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION:
                StrFixed = _T("Boot");
                break;
            default:
                StrFixed = _T("New?");
                break;
        }
        _stprintf_s(StrBuf, sizeof(StrBuf)/sizeof(StrBuf[0]), _T(" %4s"), StrFixed);
        SdirPasteStr(Buffer, StrBuf, Attributes, 5);
    }
    return 5;
}

ULONG
SdirDisplayStreamCount (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR Str[5];

    if (Buffer) {
        if (Entry->StreamCount >= 1000) {
            _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" >1k"));
        } else {
            _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %03i"), Entry->StreamCount);
        }
        SdirPasteStr(Buffer, Str, Attributes, 4);
    }
    return 4;
}

ULONG
SdirDisplayUsn(
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayHex64(Buffer, Attributes, &Entry->Usn);
}

ULONG
SdirDisplayVersion(
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    TCHAR Str[40];

    if (Buffer) {
        _stprintf_s(Str,
                    sizeof(Str)/sizeof(Str[0]),
                    _T(" %02i.%02i.%05i.%05i %c%c"),
                    Entry->FileVersion.HighPart >> 16,
                    Entry->FileVersion.HighPart & 0xffff,
                    Entry->FileVersion.LowPart >> 16,
                    Entry->FileVersion.LowPart & 0xffff,
                    Entry->FileVersionFlags & VS_FF_DEBUG ? 'D' : '-',
                    Entry->FileVersionFlags & VS_FF_PRERELEASE ? 'P' : '-');

        SdirPasteStr(Buffer, Str, Attributes, 21);
    }
    return 21;
}

ULONG
SdirDisplaySummary(
    __in WORD DefaultAttributes
    )
{
    SDIR_FMTCHAR Buffer[200];
    TCHAR Str[100];
    DWORD Len;
    LARGE_INTEGER Size;
    ULONG CurrentChar = 0;

    _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %i "), Summary->NumFiles);
    Len = (DWORD)_tcslen(Str);
    SdirPasteStr(&Buffer[CurrentChar], Str, Opts->FtNumberFiles.HighlightColor, Len);
    CurrentChar += Len;

    SdirPasteStr(&Buffer[CurrentChar], _T("files,"), DefaultAttributes, 6);
    CurrentChar += 6;

    _stprintf_s(Str, sizeof(Str)/sizeof(Str[0]), _T(" %i "), Summary->NumDirs);
    Len = (DWORD)_tcslen(Str);
    SdirPasteStr(&Buffer[CurrentChar], Str, Opts->FtNumberFiles.HighlightColor, Len);
    CurrentChar += Len;

    SdirPasteStr(&Buffer[CurrentChar], _T("dirs,"), DefaultAttributes, 5);
    CurrentChar += 5;

    Size.HighPart = 0;
    SdirFileSizeFromLargeInt(&Size) = Summary->TotalSize;

    CurrentChar += SdirDisplayGenericSize(&Buffer[CurrentChar], Opts->FtFileSize.HighlightColor, &Size);
    SdirPasteStr(&Buffer[CurrentChar], _T(" used,"), DefaultAttributes, 6);
    CurrentChar += 6;

    if (Opts->FtCompressedFileSize.Flags & SDIR_FEATURE_DISPLAY) {
        Size.HighPart = 0;
        SdirFileSizeFromLargeInt(&Size) = Summary->CompressedSize;

        CurrentChar += SdirDisplayGenericSize(&Buffer[CurrentChar], Opts->FtCompressedFileSize.HighlightColor, &Size);
        SdirPasteStr(&Buffer[CurrentChar], _T(" compressed,"), DefaultAttributes, 12);
        CurrentChar += 12;
    }

    CurrentChar += SdirDisplayGenericSize(&Buffer[CurrentChar], Opts->FtFileSize.HighlightColor, &Summary->VolumeSize);
    SdirPasteStr(&Buffer[CurrentChar], _T(" vol size,"), DefaultAttributes, 10);
    CurrentChar += 10;

    CurrentChar += SdirDisplayGenericSize(&Buffer[CurrentChar], Opts->FtFileSize.HighlightColor, &Summary->FreeSize);
    SdirPasteStr(&Buffer[CurrentChar], _T(" vol free"), DefaultAttributes, 9);
    CurrentChar += 9;

    SdirWrite(Buffer, CurrentChar);

    return CurrentChar;
}

ULONG
SdirDisplayFileWriteDate (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayFileDate(Buffer, Attributes, &Entry->WriteTime);
}

ULONG
SdirDisplayFileWriteTime (
    PSDIR_FMTCHAR Buffer,
    __in WORD Attributes,
    __in PSDIR_DIRENT Entry
    )
{
    return SdirDisplayFileTime(Buffer, Attributes, &Entry->WriteTime);
}

//
//  The below table corresponds to the supported options in the program.
//  Each option has a flags value describing whether to collect or display
//  the piece of metadata, a default, a switch to turn display on or off
//  or sort by it, a callback function to tell how much space it will need,
//  an optional sort compare function, and some help text.
//
//  File names, extensions and attributes are always collected, because
//  that's how we determine colors.
//

#define OPT_OS(x) FIELD_OFFSET(SDIR_OPTS, x)

const SDIR_OPT SdirOptions[] = {

#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtAllocatedRangeCount),      _T("ac"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayAllocatedRangeCount,  SdirCollectAllocatedRangeCount,
        SdirCompareAllocatedRangeCount,  NULL,                          SdirGenerateAllocatedRangeCount,
        "allocated range count"},
#endif

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtAccessDate),               _T("ad"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_GREEN|FOREGROUND_BLUE},
        SdirDisplayFileAccessDate,       SdirCollectAccessTime,
        SdirCompareAccessDate,           NULL,                          SdirGenerateAccessDate,
        "access date"},
#endif

#if SDIR_FEATURE_LEVEL >= 4
    {OPT_OS(FtArch),                     _T("ar"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY},
        SdirDisplayArch,                 SdirCollectArch,
        SdirCompareArch,                 NULL,                          SdirGenerateArch,
        "CPU architecture"},
#endif

#if SDIR_FEATURE_LEVEL >= 2
    {OPT_OS(FtAllocationSize),           _T("as"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY},
        SdirDisplayAllocationSize,       SdirCollectAllocationSize,
        SdirCompareAllocationSize,       NULL,                          SdirGenerateAllocationSize,
        "allocation size"},
#endif

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtAccessTime),               _T("at"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_GREEN|FOREGROUND_BLUE},
        SdirDisplayFileAccessTime,       SdirCollectAccessTime,
        SdirCompareAccessTime,           NULL,                          SdirGenerateAccessTime,
        "access time"},
#endif

    {OPT_OS(FtBriefAlternate),           _T("ba"), {0, BACKGROUND_BLUE},
        NULL,                            NULL,                 
        NULL,                            NULL,                          NULL,
        "brief alternate mask"},

#if SDIR_FEATURE_LEVEL >= 4
    {OPT_OS(FtCompressionAlgorithm),     _T("ca"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayCompressionAlgorithm, SdirCollectCompressionAlgorithm,
        SdirCompareCompressionAlgorithm, NULL,                          SdirGenerateCompressionAlgorithm,
        "compression algorithm"},
#endif
    
#if SDIR_FEATURE_LEVEL >= 1
    {OPT_OS(FtCreateDate),               _T("cd"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayFileCreateDate,       SdirCollectCreateTime,
        SdirCompareCreateDate,           NULL,                          SdirGenerateCreateDate,
        "create date"},
#endif

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtCompressedFileSize),       _T("cs"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY},
        SdirDisplayCompressedFileSize,   SdirCollectCompressedFileSize,
        SdirCompareCompressedFileSize,   NULL,                          SdirGenerateCompressedFileSize,
        "compressed size"},
#endif

#if SDIR_FEATURE_LEVEL >= 1
    {OPT_OS(FtCreateTime),               _T("ct"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayFileCreateTime,       SdirCollectCreateTime,
        SdirCompareCreateTime,           NULL,                          SdirGenerateCreateTime,
        "create time"},
#endif

#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtEffectivePermissions),     _T("ep"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayEffectivePermissions, SdirCollectEffectivePermissions,
        SdirCompareEffectivePermissions, SdirBitwiseEffectivePermissions, SdirGenerateEffectivePermissions,
        "effective permissions"},
#endif

    {OPT_OS(FtError),                     _T("er"), {0, FOREGROUND_RED|FOREGROUND_INTENSITY},
        NULL,                            NULL,                 
        NULL,                            NULL,                          NULL,
        "errors"},

    {OPT_OS(FtFileAttributes),           _T("fa"), {SDIR_FEATURE_COLLECT|SDIR_FEATURE_ALLOW_DISPLAY, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY},
        SdirDisplayFileAttributes,       SdirCollectFileAttributes,
        SdirCompareFileAttributes,       SdirBitwiseFileAttributes,     SdirGenerateFileAttributes,
        "file attributes"},

#if SDIR_FEATURE_LEVEL >= 4
    {OPT_OS(FtFragmentCount),            _T("fc"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayFragmentCount,        SdirCollectFragmentCount,
        SdirCompareFragmentCount,        NULL,                          SdirGenerateFragmentCount,
        "fragment count"},
#endif

    {OPT_OS(FtFileExtension),            _T("fe"), {SDIR_FEATURE_DISPLAY|SDIR_FEATURE_COLLECT|SDIR_FEATURE_ALLOW_SORT|SDIR_FEATURE_FIXED_COLOR, 0},
        NULL,                            SdirCollectFileExtension,
        SdirCompareFileExtension,        NULL,                          SdirGenerateFileExtension,
        "file extension"},

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtFileId),                   _T("fi"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayFileId,               SdirCollectFileId,
        SdirCompareFileId,               NULL,                          NULL,
        "file id"},
#endif

    {OPT_OS(FtFileName),                 _T("fn"), {SDIR_FEATURE_DISPLAY|SDIR_FEATURE_COLLECT|SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT|SDIR_FEATURE_USE_FILE_COLOR, 0},
        NULL,                            SdirCollectFileName,
        SdirCompareFileName,             SdirBitwiseFileName,           SdirGenerateFileName,
        "file name"},

    {OPT_OS(FtFileSize),                 _T("fs"), {SDIR_FEATURE_DISPLAY|SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY},
        SdirDisplayFileSize,             SdirCollectFileSize,
        SdirCompareFileSize,             NULL,                          SdirGenerateFileSize,
        "file size"},

    {OPT_OS(FtGrid),                     _T("gr"), {0, FOREGROUND_GREEN},
        NULL,                            NULL,                 
        NULL,                            NULL,                          NULL,
        "grid"},

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtLinkCount),                _T("lc"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayLinkCount,            SdirCollectLinkCount,
        SdirCompareLinkCount,            NULL,                          SdirGenerateLinkCount,
        "link count"},
#endif

    {OPT_OS(FtNumberFiles),              _T("nf"), {0, FOREGROUND_GREEN|FOREGROUND_INTENSITY},
        NULL,                            NULL,                 
        NULL,                            NULL,                          NULL,
        "number files"},

#if SDIR_FEATURE_LEVEL >= 2
#ifdef UNICODE
    {OPT_OS(FtNamedStreams),             _T("ns"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_FIXED_COLOR, 0},
        NULL,                            NULL,                   
        NULL,                            NULL,                          NULL,
        "named streams"},
#endif
#endif

#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtObjectId),                _T("oi"), {SDIR_FEATURE_COLLECT|SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayObjectId,             SdirCollectObjectId,
        SdirCompareObjectId,             NULL,                          SdirGenerateObjectId,
        "object id"},
#endif

#if SDIR_FEATURE_LEVEL >= 4
    {OPT_OS(FtOsVersion),                _T("os"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayOsVersion,            SdirCollectOsVersion,
        SdirCompareOsVersion,            NULL,                          SdirGenerateOsVersion,
        "minimum OS version"},
#endif

#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtOwner),                    _T("ow"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayOwner,                SdirCollectOwner,
        SdirCompareOwner,                NULL,                          SdirGenerateOwner,
        "owner"},
#endif

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtReparseTag),               _T("rt"), {SDIR_FEATURE_COLLECT|SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayReparseTag,           SdirCollectReparseTag,
        SdirCompareReparseTag,           NULL,                          SdirGenerateReparseTag,
        "reparse tag"},
#endif

#if SDIR_FEATURE_LEVEL >= 3
#ifdef UNICODE
    {OPT_OS(FtStreamCount),              _T("sc"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayStreamCount,          SdirCollectStreamCount,
        SdirCompareStreamCount,          NULL,                          SdirGenerateStreamCount,
        "stream count"},
#endif
#endif

    {OPT_OS(FtSummary),                  _T("sm"), {SDIR_FEATURE_DISPLAY|SDIR_FEATURE_COLLECT|SDIR_FEATURE_ALLOW_DISPLAY, FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE},
        NULL,                            NULL,
        NULL,                            NULL,                          NULL,
        "summary"},

#if SDIR_FEATURE_LEVEL >= 2
    {OPT_OS(FtShortName),                _T("sn"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT|SDIR_FEATURE_USE_FILE_COLOR, 0},
        SdirDisplayShortName,            SdirCollectShortName,
        SdirCompareShortName,            NULL,                          SdirGenerateShortName,
        "short name"},
#endif

#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtSubsystem),                _T("ss"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplaySubsystem,            SdirCollectSubsystem,
        SdirCompareSubsystem,            NULL,                          SdirGenerateSubsystem,
        "subsystem"},
#endif

#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtUsn),                      _T("us"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayUsn,                  SdirCollectUsn,
        SdirCompareUsn,                  NULL,                          SdirGenerateUsn,
        "USN"},
#endif

#if SDIR_FEATURE_LEVEL >= 4
    {OPT_OS(FtVersion),                  _T("vr"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_RED|FOREGROUND_GREEN},
        SdirDisplayVersion,              SdirCollectVersion,
        SdirCompareVersion,              NULL,                          SdirGenerateVersion,
        "version"},
#endif

#if SDIR_FEATURE_LEVEL >= 1
    {OPT_OS(FtWriteDate),                _T("wd"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_GREEN},
        SdirDisplayFileWriteDate,        SdirCollectWriteTime,
        SdirCompareWriteDate,            NULL,                          SdirGenerateWriteDate,
        "write date"},
#endif

#if SDIR_FEATURE_LEVEL >= 1
    {OPT_OS(FtWriteTime),                _T("wt"), {SDIR_FEATURE_ALLOW_DISPLAY|SDIR_FEATURE_ALLOW_SORT, FOREGROUND_GREEN},
        SdirDisplayFileWriteTime,        SdirCollectWriteTime,
        SdirCompareWriteTime,            NULL,                          SdirGenerateWriteTime,
        "write time"},
#endif
};

DWORD
SdirGetNumSdirOptions()
{
    return sizeof(SdirOptions)/sizeof(SdirOptions[0]);
}

//
//  This table is really quite redundant, but it corresponds to the order
//  in which we will render each piece of metadata.  The table above is
//  the order we display help.
//


const SDIR_EXEC SdirExec[] = {
#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtFileId),               SdirDisplayFileId},
    {OPT_OS(FtUsn),                  SdirDisplayUsn},
#endif
    {OPT_OS(FtFileSize),             SdirDisplayFileSize},
#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtCompressedFileSize),   SdirDisplayCompressedFileSize},
#endif
#if SDIR_FEATURE_LEVEL >= 2
    {OPT_OS(FtAllocationSize),       SdirDisplayAllocationSize},
#endif
    {OPT_OS(FtFileAttributes),       SdirDisplayFileAttributes},
#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtObjectId),             SdirDisplayObjectId},
#endif
#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtReparseTag),           SdirDisplayReparseTag},
    {OPT_OS(FtLinkCount),            SdirDisplayLinkCount},
    {OPT_OS(FtStreamCount),          SdirDisplayStreamCount},
#endif
#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtOwner),                SdirDisplayOwner},
    {OPT_OS(FtEffectivePermissions), SdirDisplayEffectivePermissions},
#endif
#if SDIR_FEATURE_LEVEL >= 1
    {OPT_OS(FtCreateDate),           SdirDisplayFileCreateDate},
    {OPT_OS(FtCreateTime),           SdirDisplayFileCreateTime},
    {OPT_OS(FtWriteDate),            SdirDisplayFileWriteDate},
    {OPT_OS(FtWriteTime),            SdirDisplayFileWriteTime},
#endif
#if SDIR_FEATURE_LEVEL >= 3
    {OPT_OS(FtAccessDate),           SdirDisplayFileAccessDate},
    {OPT_OS(FtAccessTime),           SdirDisplayFileAccessTime},
#endif
#if SDIR_FEATURE_LEVEL >= 4
    {OPT_OS(FtVersion),              SdirDisplayVersion},
    {OPT_OS(FtOsVersion),            SdirDisplayOsVersion},
    {OPT_OS(FtArch),                 SdirDisplayArch},
    {OPT_OS(FtSubsystem),            SdirDisplaySubsystem},
    {OPT_OS(FtCompressionAlgorithm), SdirDisplayCompressionAlgorithm},
    {OPT_OS(FtFragmentCount),        SdirDisplayFragmentCount},
#endif
#if SDIR_FEATURE_LEVEL >= 5
    {OPT_OS(FtAllocatedRangeCount),  SdirDisplayAllocatedRangeCount},
#endif
};

DWORD
SdirGetNumSdirExec()
{
    return sizeof(SdirExec)/sizeof(SdirExec[0]);
}

// vim:sw=4:ts=4:et:
